package dev.ece.core.sprite;

import java.util.Collection;
import java.util.Iterator;

import dev.ece.core.cells.CellGroup;
import dev.ece.core.cells.events.Mouse2DEvent;
import dev.ece.core.cells.model.UI;
import dev.ece.util.lib.Vector2f;
import dev.ece.util.lib.Vector3f;
import dev.ece.util.tools.Camera;
import dev.ece.util.tools.transformations.Translation;

/**
 * 组件类
 * @author 飘渺青衣
 * <b>E-mail：</b>3375758@qq.com
 */
@SuppressWarnings("serial")
public abstract class Component<O, M extends UI<O>> extends CellGroup<Component<?, ?>> implements Mouse2DEvent {
	
	private Component<?, ? extends UI<?>> parent;
	
	public final Vector2f position = new Vector3f();
	
	private M ui;
	
	private O data;
	
	public Component(M ui) {
		this.ui = ui;
		this.transformations.add(new Translation((Vector3f) position));
	}
	
	@Override
	public void onInitialize() {
		this.ui.initialize();
	}

	@Override
	public void onRender(Camera camera) {
		this.ui.render(camera, data);
	}

	@Override
	public void destroy() {
		super.destroy();
		if(ui != null) {
			ui.destroy();
			ui = null;
		}
	}
	
	@Override
	public synchronized boolean add(Component<?, ?> e) {
		e.parent = this;
		return super.add(e);
	}

	@Override
	public void add(int index, Component<?, ?> element) {
		element.parent = this;
		super.add(index, element);
	}

	@Override
	public synchronized void addElement(Component<?, ?> obj) {
		obj.parent = this;
		super.addElement(obj);
	}

	@Override
	public synchronized boolean addAll(Collection<? extends Component<?, ?>> c) {
		Iterator<? extends Component<?, ?>> it = c.iterator();
		while(it.hasNext()) {
			it.next().setParent(this);
		}
		return super.addAll(c);
	}

	@Override
	public synchronized boolean addAll(int index, Collection<? extends Component<?, ?>> c) {
		Iterator<? extends Component<?, ?>> it = c.iterator();
		while(it.hasNext()) {
			it.next().setParent(this);
		}
		return super.addAll(index, c);
	}

	
	
	@Override
	public boolean remove(Object o) {
		boolean result = super.remove(o);
		if(result) {
			((Component<?, ?>) o).setParent(null);
		}
		return result;
	}

	@Override
	public synchronized Component<?, ?> remove(int index) {
		Component<?, ?> result = super.remove(index);
		if(result != null) {
			result.setParent(null);
		}
		return result;
	}

	@Override
	public synchronized boolean removeAll(Collection<?> c) {
		boolean result = super.removeAll(c);
		if(result) {
			for(Object o: c) {
				((Component<?, ?>) o).setParent(null);
			}
		}
		return result;
	}

	@Override
	public void clear() {
		for(Component<?, ?> comp: this) {
			comp.setParent(null);
		}
		super.clear();
	}

	public boolean isMouseInComponent(float x, float y) {
		Vector2f absolutePosition = this.getAbsolutePosition();
		return Math.abs(x - absolutePosition.getX()) <= this.getWidth() / 2 &&
				Math.abs(y - absolutePosition.getY()) <= this.getHeight() / 2;
	}
	
	public boolean mouseMove(float x, float y) {
		if(this.isVisible() && isMouseInComponent(x, y)) {
			if(!this.isEmpty()) {
				Iterator<Component<?, ?>> it = this.reverseIterator();
				boolean result = false;
				while(it.hasNext()) {
					if(it.next().mouseMove(x, y)) {
						result = true;
					}
				}
				if(result) {
					return true;
				}
			}
			return this.onMouseMove(x, y);
		}
		return false;
	}

	public boolean mouseButtonDown(int button, float x, float y) {
		if(this.isVisible() && isMouseInComponent(x, y)) {
			if(!this.isEmpty()) {
				Iterator<Component<?, ?>> it = this.reverseIterator();
				boolean result = false;
				while(it.hasNext()) {
					if(it.next().mouseButtonDown(button, x, y)) {
						result = true;
					}
				}
				if(result) {
					return true;
				}
			}
			return this.onMouseButtonDown(button, x, y);
		}
		return false;
	}

	public boolean mouseButtonUp(int button, float x, float y) {
		if(this.isVisible()) {
			if(!this.isEmpty()) {
				Iterator<Component<?, ?>> it = this.reverseIterator();
				while(it.hasNext()) {
					it.next().mouseButtonUp(button, x, y);
				}
			}
			this.onMouseButtonUp(button, x, y);
		}
		return false;
	}

	public boolean mouseButtonClick(int button, float x, float y) {
		if(this.isVisible() && isMouseInComponent(x, y)) {
			if(!this.isEmpty()) {
				Iterator<Component<?, ?>> it = this.reverseIterator();
				boolean result = false;
				while(it.hasNext()) {
					if(it.next().mouseButtonClick(button, x, y)) {
						result = true;
					}
				}
				if(result) {
					return true;
				}
			}
			return this.onMouseButtonClick(button, x, y);
		}
		return false;
	}

	public boolean mouseScroll(double xoffset, double yoffset, float x, float y) {
		if(this.isVisible() && isMouseInComponent(x, y)) {
			if(!this.isEmpty()) {
				Iterator<Component<?, ?>> it = this.reverseIterator();
				boolean result = false;
				while(it.hasNext()) {
					if(it.next().mouseScroll(xoffset, yoffset, x, y)) {
						result = true;
					}
				}
				if(result) {
					return true;
				}
			}
			return this.onMouseScroll(xoffset, yoffset, x, y);
		}
		return false;
	}
	
	@Override
	public boolean onMouseMove(float x, float y) {
		return false;
	}

	@Override
	public boolean onMouseButtonDown(int button, float x, float y) {
		return false;
	}

	@Override
	public boolean onMouseButtonUp(int button, float x, float y) {
		return false;
	}

	@Override
	public boolean onMouseButtonClick(int button, float x, float y) {
		return false;
	}

	@Override
	public boolean onMouseScroll(double xoffset, double yoffset, float x, float y) {
		return false;
	}

	public Vector2f getAbsolutePosition() {
		return parent == null ? position : getPositionWithParentPosition();
	}
	
	protected Vector2f getPositionWithParentPosition() {
		Vector2f position = parent.getAbsolutePosition();
		return new Vector2f(position.getX() + this.position.getX(), position.getY() + this.position.getY());
	}
	
	public Component<?, ? extends UI<?>> getParent() {
		return parent;
	}

	protected void setParent(Component<?, ? extends UI<?>> parent) {
		this.parent = parent;
	}

	public O getData() {
		return data;
	}

	public void setData(O data) {
		this.data = data;
	}

	public M getUi() {
		return ui;
	}

	protected void setUi(M ui) {
		this.ui = ui;
	}
	
	public abstract float getWidth();
	
	public abstract float getHeight();
	
}
